#include "tcp_log.h"
#include "eth_connect.h"
#include "wifi_connect.h"
#define TAG "TCP_LOG"
#define LOG_QUEUE_SIZE 10
#define LOG_BUFF_SIZE 1024
#define TCP_PORT 10001

static char log_buffer[LOG_BUFF_SIZE];
static char log_buffer2[LOG_BUFF_SIZE];
static int log_sock = -1;

vprintf_like_t old = NULL;
TaskHandle_t tcp_log_send_task_handle = NULL;
static xQueueHandle tcp_log_queue;

int esp_gpio_log_vprintf(const char* fmt, va_list vp)
{
    int len = vsnprintf(log_buffer, sizeof(log_buffer), fmt, vp);
    if ((size_t)len > sizeof(log_buffer) - 1) {
        log_buffer[sizeof(log_buffer) - 1] = '\0';
    }
    if (tcp_log_queue && xQueueSend(tcp_log_queue, (void*)log_buffer, 1000 / portTICK_PERIOD_MS) != pdPASS) {
        printf("tcp_log_queue send failed\n");
        esp_log_set_vprintf(old);
    }
    vTaskDelay(pdMS_TO_TICKS(20));
    return len;
}

void tcp_log_send_task(void* args)
{
    size_t len = 0;
    int s = *(int*)args;
    printf("tcp_log_send_task start\n");
    ssize_t res = 0;
    while (log_sock > 0) {
        if (xQueueReceive(tcp_log_queue, log_buffer2, pdMS_TO_TICKS(3000)) == pdPASS) {
            if (log_sock < 0) {
                break;
            }
            len = strlen(log_buffer2);
            if (log_sock > 0) {
                res = send(s, (const void*)log_buffer2, len > sizeof(log_buffer2) ? sizeof(log_buffer2) : len, MSG_DONTWAIT);
                if (res < 0) {
                    perror("log send error");
                    shutdown(s, 0);
                    close(s);
                }
            }
        }
    }
    printf("tcp_log_send_task end\n");
    xQueueReset(tcp_log_queue);
    vQueueDelete(tcp_log_queue);
    tcp_log_queue = NULL;
    vTaskDelete(NULL);
}

static void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[128] = { 0 };
    old = esp_log_set_vprintf(esp_gpio_log_vprintf);
    vprintf_like_t old2 = NULL;
    printf("old = %p\n", old);
    if (tcp_log_queue == NULL)
        tcp_log_queue = xQueueCreate(LOG_QUEUE_SIZE, sizeof(log_buffer));
    xTaskCreate(tcp_log_send_task, "tcp_log_send_task", 1024 * 3, &sock, 10, &tcp_log_send_task_handle);
    log_sock = sock;

    while (true) {
        int r = read(sock, rx_buffer, sizeof(rx_buffer));
        if (r <= 0) {
            break;
        } else {
            if (strlen(rx_buffer) > 0 && strlen(rx_buffer) <= 128) {
                if (strstr(rx_buffer, "RS485_LOG=1") != NULL) {
                    RS485_LOG = 1;
                } else if (strstr(rx_buffer, "RS485_LOG=0") != NULL) {
                    RS485_LOG = 0;
                }

                if (strstr(rx_buffer, "MQTT_LOG=1") != NULL) {
                    MQTT_LOG = 1;
                } else if (strstr(rx_buffer, "MQTT_LOG=0") != NULL) {
                    MQTT_LOG = 0;
                }

                if (strstr(rx_buffer, "ANA_LOG=1") != NULL) {
                    ANA_LOG = 1;
                } else if (strstr(rx_buffer, "ANA_LOG=0") != NULL) {
                    ANA_LOG = 0;
                }

                if (strstr(rx_buffer, "MEM_LOG=1") != NULL) {
                    MEM_LOG = 1;
                } else if (strstr(rx_buffer, "MEM_LOG=0") != NULL) {
                    MEM_LOG = 0;
                }

                if (strstr(rx_buffer, "PHY_LOG=1") != NULL) {
                    PHY_LOG = 1;
                } else if (strstr(rx_buffer, "PHY_LOG=0") != NULL) {
                    PHY_LOG = 0;
                }

                if (strstr(rx_buffer, "TCP_LOG=1") != NULL) {
                    TCP_LOG = 1;
                } else if (strstr(rx_buffer, "TCP_LOG=0") != NULL) {
                    TCP_LOG = 0;
                }
                send(sock, (const void*)rx_buffer, r, 0);
            }
        }
    }
    log_sock = -1;
    esp_log_set_vprintf(old);
}

int listen_sock = 0;
uint8_t mTcpLogServerListened = 0;
int tcp_log_server_init(int addr_family)
{
    int ip_protocol = 0;
    struct sockaddr_storage dest_addr;
    if (addr_family == AF_INET) {
        struct sockaddr_in* dest_addr_ip4 = (struct sockaddr_in*)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(TCP_PORT);
        ip_protocol = IPPROTO_IP;
    }
    listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        mTcpLogServerListened = 0;
        shutdown(listen_sock, SHUT_RDWR);
        close(listen_sock);
        return -1;
    }
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        mTcpLogServerListened = 0;
        shutdown(listen_sock, SHUT_RDWR);
        close(listen_sock);
        return -1;
    }
    ESP_LOGI(TAG, "Socket bind, port %d", TCP_PORT);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        mTcpLogServerListened = 0;
        shutdown(listen_sock, SHUT_RDWR);
        close(listen_sock);
        return -1;
    }
    mTcpLogServerListened = 1;
    return 0;
}

TaskHandle_t tcp_log_task_handle = NULL;
#define KEEPALIVE_IDLE 5 // CONFIG_EXAMPLE_KEEPALIVE_IDLE //tcp设置连接上如果没有数据发送的话，多久后发送keepalive探测分组（s）
#define KEEPALIVE_INTERVAL 5 // CONFIG_EXAMPLE_KEEPALIVE_INTERVAL//tcp前后两次探测之间的时间间隔（s）
#define KEEPALIVE_COUNT 2 // CONFIG_EXAMPLE_KEEPALIVE_COUNT //tcp关闭一个非活跃连接之前的最大重试次数（s）
void tcp_log_task(void* pvParameters)
{
    RS485_LOG = 0; // 485报文日志
    ANA_LOG = 0; // 数据类型解析日志
    MQTT_LOG = 0; // mqtt响应日志
    HTTP_OPEN = 0; // http开关
    MEM_LOG = 0; // 打印内存大小
    PHY_LOG = 0;
    TCP_LOG = 0;
    char addr_str[32];
    int addr_family = (int)pvParameters;
    int keepAlive = 1;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepIdle = KEEPALIVE_IDLE;
    int keepCount = KEEPALIVE_COUNT;
    uint8_t mConnectedLocal = 0;
    while (1) {
        mConnectedLocal = Eth_Connected_Status() | Wifi_Connected_Status() | New_TC_AP_Status();
        if (mConnectedLocal) {
            if (!mTcpLogServerListened) {
                tcp_log_server_init(addr_family);
                vTaskDelay(pdMS_TO_TICKS(1 * 1000));
            } else {
                ESP_LOGI(TAG, "Socket listening");
                struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
                socklen_t addr_len = sizeof(source_addr);
                int sock = accept(listen_sock, (struct sockaddr*)&source_addr, &addr_len);
                if (sock < 0) {
                    ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
                    shutdown(listen_sock, SHUT_RDWR);
                    close(listen_sock);
                    mTcpLogServerListened = 0;
                } else {
                    // Set tcp keepalive option
                    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
                    setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
                    setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
                    setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
                    // Convert ip address to string
                    if (source_addr.ss_family == PF_INET) {
                        inet_ntoa_r(((struct sockaddr_in*)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
                    }
                    ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
                    do_retransmit(sock);
                    shutdown(sock, 0);
                    close(sock);
                    log_sock = -1;
                }
                vTaskDelay(pdMS_TO_TICKS(3 * 1000));
            }
        }
        vTaskDelay(pdMS_TO_TICKS(1 * 1000));
    }
}

void clear_tcp_info(int iFd)
{
    for (int i = 0; i < 8; i++) {
        if (iFd == tcp_info_buff[i].connect_fd) {
            memset(tcp_info_buff + i, 0, sizeof(J_tcp_connect_info_t));
        }
    }
}